#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/archive/binary_iarchive.hpp>

#include <cmath>
#include <fstream>
#include <map>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

typedef map<string, int> string_int_map;
map<string, string_int_map > n_map;
map<string, map<int,double> > u_prob_map;
double prob_n;
double prob_p;

int to_int(const string& s){
  istringstream iss(s);
  int r;
  iss >> r;
  return r;
}

double get_user_prob(const string& uid, int rating){
  if(u_prob_map.find(uid) == u_prob_map.end())
    return 0;


  double r = u_prob_map[uid][rating];
  return r;
}

double normalize_prob(double v){
  return -log10(v);
}


double user_positive_prediction(const string& uid){
  if(n_map.find(uid) == n_map.end())
    return 0;

  map<string,int> m = n_map[uid];
  double r = 0;
  for(map<string,int>::iterator i = m.begin();
      i != m.end(); i++){
    string n_id = i->first;
    double wt = i->second;
    double v = wt * get_user_prob(n_id, 1);
    if(v > 0)
      r += normalize_prob(v);
  }
  double vv = get_user_prob(uid,1);
  if(vv > 0)
    r += normalize_prob(vv);

  return r;
}


double user_negative_prediction(const string& uid){
  if(n_map.find(uid) == n_map.end())
    return 0;

  map<string,int> m = n_map[uid];
  double r = 0;
  for(map<string,int>::iterator i = m.begin();
      i != m.end(); i++){
    string n_id = i->first;
    double wt = i->second;
    double v = wt * get_user_prob(n_id, -1);
    if(v > 0)
      r += normalize_prob(v);
  }
  double vv = get_user_prob(uid,-1);
  if(vv > 0)
    r += normalize_prob(vv);
  return r;
}

int predicted_rating(const string& uid){
  double p_prediction = user_positive_prediction(uid);
  double n_prediction = user_negative_prediction(uid);
  cout << endl << p_prediction << " " << n_prediction;
  if(p_prediction > n_prediction)
    return 1;
  return -1;
}

void calculate_prob(){
  map<string , map<int,double> >::iterator i;
  for(i = u_prob_map.begin(); i != u_prob_map.end(); i++){
    map<int,double> & m = i->second;
    int p_count = m[1];
    int n_count = m[-1];
    int t_count = p_count + n_count;
    m[1] = (double)p_count / (double)t_count;
    m[-1] = (double)p_count / (double)t_count;
  }
}

void split(const string& s, char c, vector<string>& v){
  int i = 0;
  int j = s.find(c);
  while(j != string::npos){
    string ss = s.substr(i,j-i);
    v.push_back(ss);
    i = j+1;
    j = s.find(c,i);
    if(j == string::npos)
      v.push_back(s.substr(i,s.length()-i));
  }
}

int main(int argc, char** argv){

  cout << endl << "loading user graph";
  ifstream ifs(argv[1],ios::binary);
  boost::archive::binary_iarchive oa(ifs);
  oa >> BOOST_SERIALIZATION_NVP(n_map);
  cout << endl << "loaded user graph";

  cout << endl << "loading user prob map";
  ifstream ifss(argv[2],ios::binary);
  boost::archive::binary_iarchive oaa(ifss);
  oaa >> BOOST_SERIALIZATION_NVP(u_prob_map);
  cout << endl << "loaded user prob map";

  ofstream ofs(argv[3]);

  string s;

  ifstream input(argv[4]);

  while(getline(input,s)){
    vector<string> parts;
    split(s,',',parts);
    string uid = parts[0];
    string item_id = parts[1];
    int rating = to_int(parts[2]);
    if (rating == 0)
      cout << s << endl;
    else
      ofs << uid << " " << item_id << " " << rating << " " << predicted_rating(uid) << endl;
  }
  input.close();
  ofs.close();
  cout << "clearing up memory";
  return 0;
}
